#include <iostream>
#include <vector>
#include <algorithm>
#include "types.h"
#include "help.h"
#include "test.h"
#include "sql.h"

std::vector <file> Objects;
std::vector <user> Subjects;
std::vector <SOA> ACM;
sqlite3 * db = NULL;

void init()
{
    user u; file f; SOA s;
    int nrow, ncol, i;
    char ** results, * errmsg = 0;
    sqlite3_get_table(db, "SELECT * FROM SUBJECTS", &results, &nrow, &ncol, &errmsg);
    for(i = ncol; i < ncol * nrow + ncol;)
    {
        u.name = std::string(results[i++]);
        u.password = std::string(results[i++]);
        Subjects.push_back(u);
    }
    sqlite3_free_table(results);
    sqlite3_get_table(db, "SELECT * FROM OBJECTS", &results, &nrow, &ncol, &errmsg);
    for(i = ncol; i < ncol * nrow + ncol;)
    {
        f.filename = std::string(results[i++]);
        f.creator = std::string(results[i++]);
        f.content = std::string(results[i++]);
        Objects.push_back(f);
    }
    sqlite3_free_table(results);
    sqlite3_get_table(db, "SELECT * FROM ACM", &results, &nrow, &ncol, &errmsg);
    for(i = ncol; i < ncol * nrow + ncol;)
    {
        s.user = std::string(results[i++]);
        s.filename = std::string(results[i++]);
        s.access = std::stoi(std::string(results[i++]));
        s.from_to = std::string(results[i++]);
        ACM.push_back(s);
    }
    sqlite3_free_table(results);
}
std::string current_user::get_username()
{
    return u.name;
}
int current_user::create_subject(std::string &name, std::string &password)
{
    if (this->level)
    {
        fprintf(stdout, RED "[error] Normal users are not allowed to create user\n" NONE);
        return 0;
    }
    else
    {
        user u;
        u.name = name;
        u.password = password;
        Subjects.push_back(u);
        sql_exec(db, NULL, "INSERT INTO Subjects (name, password) VALUES ('%s', '%s')", name.c_str(), password.c_str());
        return 1;
    }
}
int current_user::delete_subject(std::string &username)
{
    if (this->level)
    {
        fprintf(stdout, RED "[error] Normal users are not allowed to create user\n" NONE);
        return 0;
    }
    else
    {
        std::vector <user>::iterator it;
        // delete <username, password> in subjects
        for (it = Subjects.begin(); it != Subjects.end();)
        {
            if (it->name == username)
            {
                it = Subjects.erase(it);
            }
            else
            {
                it ++;
            }
        }
        sql_exec(db, NULL, "DELETE FROM Subjects WHERE name = '%s'", username.c_str());
        std::vector <file>::iterator it2;
        // delete (creator = username) in subjects
        for (it2 = Objects.begin(); it2 != Objects.end();)
        {
            if (it2->creator == username)
            {
                it2 = Objects.erase(it2);
            }
            else
            {
                it2 ++;
            }
        }
        sql_exec(db, NULL, "DELETE FROM Objects WHERE creator = '%s'", username.c_str());
        std::vector <SOA>::iterator it3;
        // delete (user = username || from_to == username) in ACM
        for (it3 = ACM.begin(); it3 != ACM.end();)
        {
            if (it3->user == username || it3->from_to == username)
            {
                it3 = ACM.erase(it3);
            }
            else
            {
                it3 ++;
            }
        }
        sql_exec(db, NULL, "DELETE FROM ACM WHERE user = '%s' or from_to = '%s'", username.c_str(), username.c_str()); 
        return 1;
    }
}
int current_user::create_object(std::string &filename, std::string &content)
{
    file f;
    f.filename = filename;
    f.creator = (this->u).name;
    f.content = content;
    Objects.push_back(f);
    sql_exec(db, NULL, "INSERT INTO Objects (filename, creator, content) VALUES ('%s', '%s', '%s')", \
        f.filename.c_str(), f.creator.c_str(), f.content.c_str());

    SOA s;
    s.user = (this->u).name;
    s.filename = filename;
    s.access = READ;
    s.from_to = (this->u).name;
    ACM.push_back(s);
    sql_exec(
        db,\
        NULL, \
        "INSERT INTO ACM (user, filename, access, from_to) VALUES ('%s', '%s', %d, '%s')", \
        s.user.c_str(), s.filename.c_str(), READ, s.from_to.c_str() \
    );
    
    s.access = WRITE;
    ACM.push_back(s);
    sql_exec(
        db,\
        NULL, \
        "INSERT INTO ACM (user, filename, access, from_to) VALUES ('%s', '%s', %d, '%s')", \
        s.user.c_str(), s.filename.c_str(), WRITE, s.from_to.c_str() \
    );
    
    s.access = OWN;
    ACM.push_back(s);
    sql_exec(
        db,\
        NULL, \
        "INSERT INTO ACM (user, filename, access, from_to) VALUES ('%s', '%s', %d, '%s')", \
        s.user.c_str(), s.filename.c_str(), OWN, s.from_to.c_str() \
    );
    return 1;
}
int current_user::read_object(std::string &filename)
{
    SOA s;
    s.user = (this->u).name;
    s.filename = filename;
    std::vector <SOA>::iterator it;
    for (it = ACM.begin(); it != ACM.end(); it++)
    {
        if (it->filename == s.filename && it->user == s.user)
        {
            if (it->access == READ || it->access == OWN)
            {
                for(std::vector <file>::iterator it2 = Objects.begin(); it2 != Objects.end(); it2 ++)
                {
                    if (it2 ->filename == it->filename)
                    {
                        std::cout << it2->content << std::endl;
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}
int current_user::write_object(std::string &filename, std::string c)
{
    SOA s;
    s.user = (this->u).name;
    s.filename = filename;
    std::vector <SOA>::iterator it;
    std::vector <file>::iterator it2;
    // not every one has the WRITE right, so first search ACM.
    for (it = ACM.begin(); it != ACM.end(); it++)
    {
        if (it->filename == s.filename && it->user == s.user)
        {
            if (it->access == WRITE || it->access == OWN)
            {
                for(it2 = Objects.begin(); it2 != Objects.end(); it2 ++)
                {
                    if (it2 ->filename == it->filename)
                    {
                        it2->content = c;
                        // ?UPDATE COMPANY SET ADDRESS = 'Texas' WHERE ID = 6;
                        sql_exec(db, NULL, "UPDATE Objects SET content = '%s' WHERE filename = '%s'", c.c_str(), filename.c_str());                      
                        return 1;
                    }
                }
            }
        }
    }
    return 0;
}
int current_user::delete_object(std::string &filename)
{
    std::vector <file>::iterator it;
    // delete in Objects
    for (it = Objects.begin(); it != Objects.end();)
    {
        // only creator can delete object, so current_user must equal creator
        if (it->filename == filename && it->creator == u.name)
        {
            sql_exec(db, NULL, "DELETE FROM Objects WHERE filename = '%s'", filename.c_str());
            it = Objects.erase(it);
        }
        else
        {
            it ++;
        }
    }
    std::vector <SOA>::iterator it2;
    // delete in ACM
    for (it2 = ACM.begin(); it2 != ACM.end();)
    {
        if (it2->filename == filename)
        {
            it2 = ACM.erase(it2);
        }
        else
        {
            it2 ++;
        }
    }
    sql_exec(db, NULL, "DELETE FROM ACM WHERE filename = '%s'", filename.c_str());    
    return 1;
}
int current_user::revoke_right(int access, std::string &user, std::string &filename)
{
    // central
    if (flag == 0) 
    {
        // find creator of filename and creator must equal current_user
        std::vector <file>::iterator it;
        std::string creator;
        for (it = Objects.begin(); it != Objects.end();)
        {
            // in central mode, only creator can revoke right, so current_user must equal creator
            if (it->filename == filename)
            {
                if (it->creator == u.name)
                {
                    creator = u.name;
                    break;
                }
                else
                {
                    fprintf(stdout, RED "[error] In central mode, only creator of file can transfer right\n" NONE);
                    return 0;
                }
            }
            else
            {
                it ++;
            }
        }
        std::vector <SOA>::iterator it3;
        // delete <user, access, filename, current_user> in ACM
        for (it3 = ACM.begin(); it3 != ACM.end();)
        {
            if (it3->user == user && it3->from_to == (this->u).name && it3->access == access && it3->filename == filename)
            {
                sql_exec(db, NULL,\
                    "DELETE FROM ACM WHERE user = '%s' and from_to = '%s' and access = %d and filename = '%s'", \
                    user.c_str(), it3->from_to.c_str(), access, filename.c_str()
                );
                it3 = ACM.erase(it3);
                break;
            }
            else
            {
                it3 ++;
            }
        }
        return 1;
    }
    // distributed
    else
    {
        std::vector <SOA>::iterator it3;
        std::string sk = (this->u).name;
        std::string username = user;
        // delete <user, access, filename, current_user> in ACM
        for (it3 = ACM.begin(); it3 != ACM.end();)
        {
            if (it3->user == username && it3->access == access && it3->filename == filename && it3->from_to == sk)
            {
                sql_exec(db, NULL,\
                    "DELETE FROM ACM WHERE user = '%s' and from_to = '%s' and access = %d and filename = '%s'", \
                    it3->user.c_str(), it3->from_to.c_str(), it3->access, it3->filename.c_str()
                );
                it3 = ACM.erase(it3);
                break;
            }
            else
            {
                it3 ++;
            }
        }
        int found = 0;
        for (it3 = ACM.begin(); it3 != ACM.end(); it3 ++)
        {
            if (it3->user == username && it3->access == access && it3->filename == filename)
            {
                found = 1;
            }
        }
        if(!found)
        {
        // recrusive
        _start:
            std::vector <SOA>::iterator it;
            std::vector <std::string> all;
            std::string any;
            int found = 0;
            for (it = ACM.begin(); it != ACM.end();)
            {
                if (it->from_to == username && it->filename == filename && it->access == access)
                {   
                    sql_exec(db, NULL,\
                        "DELETE FROM ACM WHERE from_to = '%s' and filename = '%s' and access = %d", \
                        it->from_to.c_str(), it->filename.c_str(), it->access\
                    );
                    it = ACM.erase(it);
                    found = 1;
                    all.push_back(it->user);
                }
                else
                {
                    it ++;
                }
            }
            int len = all.size();
            for(int i = 0; i < len; i ++)
            {
                any = all[i];
                if (found)
                {
                    int found2 = 0;
                    std::vector <SOA>::iterator it2;
                    for (it2 = ACM.begin(); it2 != ACM.end(); it2 ++)
                    {
                        if (it2->user == any && it2->filename == filename && it2->access == access)
                        {   
                            found2 = 1;
                        }
                    }
                    if (!found2)
                    {
                        username = any;
                        goto _start;
                    }
                    else
                    {
                        return 1;
                    }
                }
                else
                {
                    return 1;
                }
            }
            return 1;
        }
        else
        {
            std::cout << "delete success" << std::endl;
            return 1;
        }
    }
}
int current_user::transfer_right(int access, std::string &user_to, std::string &filename)
{
    // central
    if (flag == 0)
    {
        // find creator of filename and creator must equal current_user
        std::vector <file>::iterator it;
        std::string creator;
        for (it = Objects.begin(); it != Objects.end();)
        {
            // only creator can transfer right, so current_user must equal creator
            if (it->filename == filename)
            {
                if (it->creator == u.name)
                {
                    creator = u.name;
                    break;
                }
                else
                {   
                    fprintf(stdout, RED "[error] In central mode, only creator of file can transfer right\n" NONE);
                    return 0;
                }
            }
            else
            {
                it ++;
            }
        }
        if (access == OWN)
        {
            fprintf(stdout, RED "[error] In central mode, can't transfer OWN right\n" NONE);
            return 0;
        }
        int found = 0;
        // access must be in (creator, access, file, from_to)ACM
        std::vector <SOA>::iterator iter2;
        for (iter2 = ACM.begin(); iter2 != ACM.end();)
        {
            if (iter2->filename == filename && iter2->user == creator && iter2->access == access)
            {
                found = 1;
                break;
            }
            else
            {
                iter2 ++;
            }
        }
        // add to ACM
        if (found)
        {
            SOA s;
            s.access = access;
            s.filename = filename;
            s.from_to = creator;
            s.user = user_to;
            ACM.push_back(s);
            sql_exec(db, NULL, "INSERT INTO ACM (user, filename, access, from_to) VALUES ('%s', '%s', %d, '%s')", user_to.c_str(), filename.c_str(), access, creator.c_str());
        }
        else
        {
            fprintf(stdout, RED "[error] Can not transfer a non-exist right in rights of current user\n" NONE);
            return 0;
        }
        return 1;
    }
    // distributed
    else
    {
        // OWN must be in (creator, access, file, from_to) ACM
        int found = 0;
        std::vector <SOA>::iterator iter2;
        for (iter2 = ACM.begin(); iter2 != ACM.end();)
        {
            if (iter2->filename == filename && iter2->user == (this->u).name && (iter2->access == OWN))
            {
                found = 1;
                break;
            }
            else
            {
                iter2 ++;
            }
        }
        if(found)
        {
            SOA s;
            s.access = access;
            s.filename = filename;
            s.from_to = (this->u).name;
            s.user = user_to;
            ACM.push_back(s);
            sql_exec(db, NULL, "INSERT INTO ACM (user, filename, access, from_to) VALUES ('%s', '%s', %d, '%s')", user_to.c_str(), filename.c_str(), access, s.from_to.c_str());
        }   
        else
        {
            fprintf(stdout, RED "[error] In distributed mode, only user with OWN rights can transfer right\n" NONE);
            return 0;
        }
    }
    return 1;
}
current_user::current_user(int level, int flag, user u)
{
    this->level = level;
    this->flag = flag;
    (this->u).name = u.name;
    (this->u).password = u.password;
}
current_user::~current_user() {}


// this main is only used for "make local"
int main()
{
    int level = 0;  // root(0) or normal(1)
    int flag = 0;   // central(0) or distributed(1)
    user u = {"admin", "root"}; 
    class current_user cu(level, flag, u);
    sql_connect(db);
    init();
    test_distributed();
}

